package test.progenies.ecg.asm31;

import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ATHROW;
import static org.objectweb.asm.Opcodes.DUP;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.NEW;
import static org.objectweb.asm.Opcodes.RETURN;
import static org.objectweb.asm.Opcodes.V1_6;

import java.io.IOException;
import java.io.PrintWriter;
import java.security.AccessController;
import java.security.PrivilegedAction;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.util.ASMifierClassVisitor;
import org.objectweb.asm.util.CheckClassAdapter;

import com.progenies.ecg.asm31.model.codeparts.IConfigurableClassObject;
import com.progenies.ecg.generator.ByteArrayClassLoader;
import com.progenies.ecg.model.ClassObject;

public class ASMIfierUtil
{
	
	public static ByteArrayClassLoader cl;
	
	static
	{
		cl=AccessController.doPrivileged(new PrivilegedAction<ByteArrayClassLoader>() {

			@Override
			public ByteArrayClassLoader run() {
				return new ByteArrayClassLoader(ASMIfierUtil.class.getClassLoader());
			}
		});
	}
	
	
	public static void main(String args[]) throws Exception
	{
//		byte clsBytes[]=dump();
//		Class<?> cls=cl.loadByteArray(clsBytes);
		
		printClass(ASMTestClass.class);
		
//		cls.newInstance();
	}
	
	public static void printClass(ClassObject classObject)
	{
		ClassWriter writer=new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
		ClassVisitor visitor=new CheckClassAdapter(writer);
		
		IConfigurableClassObject clsObject=(IConfigurableClassObject) classObject;
		clsObject.configure(Opcodes.V1_6, visitor);
		clsObject.generateBytecode();
		
		printClass(writer.toByteArray());
	}
	
	public static void printClass(byte classBytes[])
	{
		ClassReader reader=new ClassReader(classBytes);
		reader.accept(new ASMifierClassVisitor(new PrintWriter(System.out)), ClassReader.EXPAND_FRAMES | ClassReader.SKIP_DEBUG);
	}
	
	
	
	public static void printClass(Class<?> cls) throws IOException
	{
		printClass(cls.getName());
	}
	
	public static void printClass(String name) throws IOException
	{
		ClassReader reader=new ClassReader(name);
		reader.accept(new ASMifierClassVisitor(new PrintWriter(System.out)), ClassReader.EXPAND_FRAMES | ClassReader.SKIP_DEBUG);
	}
	
	
	
	public static final class ASMTestClass
	{
		public boolean value=false;
		
		public int instanceField;
		
		public void testException() throws ClassNotFoundException
		{
			throw new ClassNotFoundException("aa");
		}
		
		
		public void runtimeExceptionMethod()
		{
			throw new RuntimeException("Runtime Exception thrown");
		}
		
		public int method1()
		{
			int i=0;
			try
			{
				testException();
			}catch(ClassNotFoundException ex)
			{
				i=9;
			}
			return i;
		}
		
		
		public int method2()
		{
			boolean value1=true;
			int i=3;
			
			if(value1)
				i+=23;
			
			return i;
		}
		
		public boolean method3()
		{
			this.value=true;
			return this.value;
		}
		
		
		public int method4()
		{
			int i=0;
			try
			{
				i++;
				throw new RuntimeException(""+1); 
			}
			catch(Exception ex)
			{
				i++;
				////throw new RuntimeException(""+i); 
			}
			catch(Throwable ex)
			{
				i++;
			//	throw new RuntimeException(""+i); 
			}
			finally
			{
				i++;
				System.out.println("I: "+i);
			}
			return i;
			
		}
		
		public int method5()
		{
			try
			{
				this.testException();
			}
			catch(ClassNotFoundException ex1)
			{
				instanceField=8;
			}
			catch(RuntimeException ex2)
			{
				instanceField=6;
			}
			finally
			{
				instanceField--;
			}
			
			return instanceField;
		}
		
		public int method6()
		{
			try
			{
				this.testException();
			}
			catch(ClassNotFoundException ex1)
			{
				instanceField=8;
			}
			catch(Throwable ex2)
			{
				instanceField=6;
			}
			finally
			{
				instanceField--;
			}
			
			return instanceField;
		}
		
	}
	
	
	
	public static byte[] dump () throws Exception {

		ClassWriter cw = new ClassWriter(0);
//		FieldVisitor fv;
		MethodVisitor mv;
//		AnnotationVisitor av0;

		cw.visit(V1_6, ACC_PUBLIC, "test/exceptions/exceptions/LocalVariableTestClass1", null, "java/lang/Object", null);

		{
			mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
			mv.visitCode();
			mv.visitVarInsn(ALOAD, 0);
			mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
			mv.visitInsn(RETURN);
			mv.visitMaxs(1, 1);
			mv.visitEnd();
		}
		{
			mv = cw.visitMethod(ACC_PUBLIC, "checkExceptionMethod", "()V", null, new String[] { "java/lang/ClassNotFoundException" });
			mv.visitCode();
			mv.visitFrame(Opcodes.F_NEW, 1, new Object[] {"test/exceptions/exceptions/LocalVariableTestClass1"}, 0, new Object[] {});
			mv.visitTypeInsn(NEW, "java/lang/ClassNotFoundException");
			mv.visitInsn(DUP);
			mv.visitLdcInsn("Class not found over there");
			mv.visitMethodInsn(INVOKESPECIAL, "java/lang/ClassNotFoundException", "<init>", "(Ljava/lang/String;)V");
			mv.visitInsn(ATHROW);
			Label l0 = new Label();
			mv.visitLabel(l0);
			mv.visitFrame(Opcodes.F_NEW, 0, new Object[] {}, 1, new Object[] {"java/lang/Throwable"});
			mv.visitInsn(ATHROW);
			mv.visitMaxs(3, 1);
			mv.visitEnd();
		}
		{
			mv = cw.visitMethod(ACC_PUBLIC, "runtimeExceptionMethod", "()V", null, null);
			mv.visitCode();
			mv.visitFrame(Opcodes.F_NEW, 1, new Object[] {"test/exceptions/exceptions/LocalVariableTestClass1"}, 0, new Object[] {});
			mv.visitTypeInsn(NEW, "java/lang/RuntimeException");
			mv.visitInsn(DUP);
			mv.visitLdcInsn("Runtime Exception thrown");
			mv.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "(Ljava/lang/String;)V");
			mv.visitInsn(ATHROW);
			Label l0 = new Label();
			mv.visitLabel(l0);
			mv.visitFrame(Opcodes.F_NEW, 0, new Object[] {}, 1, new Object[] {"java/lang/Throwable"});
			mv.visitInsn(ATHROW);
			mv.visitMaxs(3, 1);
			mv.visitEnd();
		}
		{
			mv=cw.visitMethod(1, "method1", "()I", null, null);
			mv.visitCode();
			Label l1=new Label();
			mv.visitLabel(l1);
			mv.visitLocalVariable("localValue", "I", null, l1, l1, 1);
			mv.visitLdcInsn(0);
			mv.visitVarInsn(54, 1);



			Label lblBody=new Label();
			Label lblEndBody=new Label();
			Label lblFinally=new Label();
			Label lblEnd=new Label();
			Label lblCatch=new Label();
			mv.visitTryCatchBlock(lblBody, lblEndBody, lblCatch, "java/lang/ClassNotFoundException");
			mv.visitLabel(lblBody);
			mv.visitVarInsn(25, 0);
			mv.visitMethodInsn(182, "test/exceptions/exceptions/LocalVariableTestClass1", "checkExceptionMethod", "()V");
			mv.visitLabel(lblEndBody);
			mv.visitJumpInsn(Opcodes.GOTO, lblFinally);
			mv.visitLabel(lblCatch);
			mv.visitLocalVariable("ex0", "Ljava/lang/ClassNotFoundException;", null, lblCatch, lblCatch, 2);
			mv.visitVarInsn(Opcodes.ASTORE, 2);
			mv.visitLdcInsn(9);
			mv.visitVarInsn(54, 1);
			mv.visitJumpInsn(Opcodes.GOTO, lblFinally);
			mv.visitLabel(lblFinally);
			mv.visitLabel(lblEnd);
			mv.visitVarInsn(21, 1);
			mv.visitInsn(172);
			mv.visitMaxs(Short.MAX_VALUE, Short.MAX_VALUE);
			mv.visitEnd();

		}
		cw.visitEnd();

		return cw.toByteArray();
		}
	
	
	

}
